home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / sunbird / components / calCompositeCalendar.js < prev    next >
Encoding:
JavaScript  |  2007-05-23  |  18.4 KB  |  572 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Oracle Corporation code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  *  Oracle Corporation
  19.  * Portions created by the Initial Developer are Copyright (C) 2004
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. //
  40. // calCompositeCalendar.js
  41. //
  42.  
  43. const calIOperationListener = Components.interfaces.calIOperationListener;
  44.  
  45. function calCompositeCalendarObserverHelper (compCalendar) {
  46.     this.compCalendar = compCalendar;
  47. }
  48.  
  49. calCompositeCalendarObserverHelper.prototype = {
  50.     QueryInterface: function (aIID) {
  51.         if (!aIID.equals(Components.interfaces.calIObserver) &&
  52.             !aIID.equals(Components.interfaces.nsISupports))
  53.         {
  54.             throw Components.results.NS_ERROR_NO_INTERFACE;
  55.         }
  56.  
  57.         return this;
  58.     },
  59.  
  60.     // We could actually reach up into our caller for method name
  61.     // and arguments, but it hardly seems worth it.
  62.     notifyObservers: function(method, args) {
  63.         this.compCalendar.mObservers.forEach(function (o) {
  64.             try { o[method].apply(o, args); }
  65.             catch (e) { }
  66.         });
  67.     },
  68.     
  69.     onStartBatch: function() {
  70.         this.notifyObservers("onStartBatch");
  71.     },
  72.  
  73.     onEndBatch: function() {
  74.         this.notifyObservers("onEndBatch");
  75.     },
  76.  
  77.     onLoad: function() {
  78.         this.notifyObservers("onLoad");
  79.     },
  80.  
  81.     onAddItem: function(aItem) {
  82.         this.notifyObservers("onAddItem", arguments);
  83.     },
  84.  
  85.     onModifyItem: function(aNewItem, aOldItem) {
  86.         this.notifyObservers("onModifyItem", arguments);
  87.     },
  88.  
  89.     onDeleteItem: function(aDeletedItem) {
  90.         this.notifyObservers("onDeleteItem", arguments);
  91.     },
  92.  
  93.     onError: function(aErrNo, aMessage) {
  94.         this.notifyObservers("onError", arguments);
  95.     }
  96. };
  97.  
  98. function calCompositeCalendar () {
  99.     this.mObserverHelper = new calCompositeCalendarObserverHelper(this);
  100.     this.wrappedJSObject = this;
  101.  
  102.     this.mCalendars = new Array();
  103.     this.mCompositeObservers = new Array();
  104.     this.mObservers = new Array();
  105.     this.mDefaultCalendar = null;
  106. }
  107.  
  108. calCompositeCalendar.prototype = {
  109.     //
  110.     // private members
  111.     //
  112.     mDefaultCalendar: null,
  113.  
  114.     //
  115.     // nsISupports interface
  116.     //
  117.     QueryInterface: function (aIID) {
  118.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  119.             !aIID.equals(Components.interfaces.calICalendarProvider) &&
  120.             !aIID.equals(Components.interfaces.calICalendar) &&
  121.             !aIID.equals(Components.interfaces.calICompositeCalendar))
  122.         {
  123.             throw Components.results.NS_ERROR_NO_INTERFACE;
  124.         }
  125.  
  126.         return this;
  127.     },
  128.  
  129.     //
  130.     // calICalendarProvider interface
  131.     //
  132.     get prefChromeOverlay() {
  133.         return null;
  134.     },
  135.  
  136.     get displayName() {
  137.         return calGetString("calendar", "compositeName");
  138.     },
  139.  
  140.     createCalendar: function comp_createCal() {
  141.         throw NS_ERROR_NOT_IMPLEMENTED;
  142.     },
  143.  
  144.     deleteCalendar: function comp_deleteCal(cal, listener) {
  145.         // You shouldn't be able to delete from the composite calendar.
  146.         throw NS_ERROR_NOT_IMPLEMENTED;
  147.     },
  148.  
  149.     //
  150.     // calICompositeCalendar interface
  151.     //
  152.  
  153.     mCalendars: null,
  154.     mDefaultCalendar: null,
  155.     mPrefPrefix: null,
  156.     mDefaultPref: null,
  157.     mActivePref: null,
  158.     
  159.     set prefPrefix (aPrefPrefix) {
  160.         if (this.mPrefPrefix) {
  161.             this.mCalendars.forEach(this.removeCalendar, this);
  162.         }
  163.         this.mPrefPrefix = aPrefPrefix;
  164.         this.mActivePref = aPrefPrefix + "-in-composite";
  165.         this.mDefaultPref = aPrefPrefix + "-default";
  166.         var mgr = getCalendarManager();
  167.         var cals = mgr.getCalendars({});
  168.  
  169.         cals.forEach(function (c) {
  170.             if (mgr.getCalendarPref(c, this.mActivePref))
  171.                 this.addCalendar(c);
  172.             if (mgr.getCalendarPref(c, this.mDefaultPref))
  173.                 this.setDefaultCalendar(c, false);
  174.         }, this);
  175.     },
  176.  
  177.     get prefPrefix () {
  178.         return this.mPrefPrefix;
  179.     },
  180.  
  181.     addCalendar: function (aCalendar) {
  182.         // check if the calendar already exists
  183.         for each (cal in this.mCalendars) {
  184.             if (aCalendar.uri.equals(cal.uri)) {
  185.                 // throw exception if calendar already exists?
  186.                 return;
  187.             }
  188.         }
  189.  
  190.         // add our observer helper
  191.         aCalendar.addObserver(this.mObserverHelper);
  192.  
  193.         this.mCalendars.push(aCalendar);
  194.         if (this.mPrefPrefix) {
  195.             getCalendarManager().setCalendarPref(aCalendar, this.mActivePref,
  196.                                          "true");
  197.         }
  198.         this.notifyObservers("onCalendarAdded", [aCalendar]);
  199.  
  200.         // if we have no default calendar, we need one here
  201.         if (this.mDefaultCalendar == null)
  202.             this.setDefaultCalendar(aCalendar, false);
  203.     },
  204.  
  205.     removeCalendar: function (aServer) {
  206.         var newCalendars = Array();
  207.         var calToRemove = null;
  208.         for each (cal in this.mCalendars) {
  209.             if (!aServer.equals(cal.uri))
  210.                 newCalendars.push(cal);
  211.             else
  212.                 calToRemove = cal;
  213.         }
  214.  
  215.         if (calToRemove) {
  216.             this.mCalendars = newCalendars;
  217.             if (this.mPrefPrefix) {
  218.                 getCalendarManager().deleteCalendarPref(calToRemove,
  219.                                                 this.mActivePref);
  220.                 getCalendarManager().deleteCalendarPref(calToRemove,
  221.                                                 this.mDefaultPref);
  222.             }   
  223.             calToRemove.removeObserver(this.mObserverHelper);
  224.             this.notifyObservers("onCalendarRemoved", [calToRemove]);
  225.         }
  226.     },
  227.  
  228.     getCalendar: function (aServer) {
  229.         for each (cal in this.mCalendars) {
  230.             if (aServer.equals(cal.uri))
  231.                 return cal;
  232.         }
  233.  
  234.         return null;
  235.     },
  236.  
  237.     get calendars() {
  238.         // return a nsISimpleEnumerator of this array.  This sucks.
  239.         // XXX make this an array, like the calendar manager?
  240.         return null;
  241.     },
  242.  
  243.     get defaultCalendar() { 
  244.         return this.mDefaultCalendar;
  245.     },
  246.  
  247.     setDefaultCalendar: function (cal, usePref) {
  248.         // don't do anything if the passed calendar is the default calendar!
  249.         if (this.mDefaultCalendar && cal && this.mDefaultCalendar.uri.equals(cal.uri))
  250.             return;
  251.         if (usePref && this.mPrefPrefix) {
  252.             if (this.mDefaultCalendar) {
  253.                 getCalendarManager().deleteCalendarPref(this.mDefaultCalendar,
  254.                                                 this.mDefaultPref);
  255.             }
  256.             // if not null set the new calendar as default in the preferences
  257.             if (cal)  {
  258.                 getCalendarManager().setCalendarPref(cal, this.mDefaultPref,
  259.                                                      "true");
  260.             }
  261.         }
  262.         this.mDefaultCalendar = cal;
  263.         this.notifyObservers("onDefaultCalendarChanged", [cal]);
  264.     },
  265.  
  266.     set defaultCalendar(v) {
  267.         this.setDefaultCalendar(v, true);
  268.     },
  269.  
  270.     //
  271.     // calICalendar interface
  272.     //
  273.     // Write operations here are forwarded to either the item's
  274.     // parent calendar, or to the default calendar if one is set.
  275.     // Get operations are sent to each calendar.
  276.     //
  277.  
  278.     get id() {
  279.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  280.     },
  281.     set id(id) {
  282.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  283.     },
  284.  
  285.     // this could, at some point, return some kind of URI identifying
  286.     // all the child calendars, thus letting us create nifty calendar
  287.     // trees.
  288.     get uri() {
  289.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  290.     },
  291.     set uri(v) {
  292.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  293.     },
  294.  
  295.     get readOnly() { 
  296.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  297.     },
  298.     set readOnly(bool) {
  299.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  300.     },
  301.  
  302.     get canRefresh() {
  303.         return true;
  304.     },
  305.  
  306.     // void addObserver( in calIObserver observer );
  307.     mCompositeObservers: null,
  308.     mObservers: null,
  309.     addObserver: function (aObserver) {
  310.         const calICompositeObserver = Components.interfaces.calICompositeObserver;
  311.         if (aObserver instanceof calICompositeObserver) {
  312.             if (this.mCompositeObservers.indexOf(aObserver) == -1) {
  313.                 var compobs = aObserver.QueryInterface (calICompositeObserver);
  314.                 this.mCompositeObservers.push(compobs);
  315.             }
  316.         }
  317.  
  318.         if (this.mObservers.indexOf(aObserver) == -1)
  319.             this.mObservers.push(aObserver);
  320.     },
  321.  
  322.     // void removeObserver( in calIObserver observer );
  323.     removeObserver: function (aObserver) {
  324.         const calICompositeObserver = Components.interfaces.calICompositeObserver;
  325.         if (aObserver instanceof calICompositeObserver)
  326.             this.mCompositeObservers = this.mCompositeObservers.filter( function (v) { return v != aObserver; } );
  327.  
  328.         this.mObservers = this.mObservers.filter( function (v) { return v != aObserver; } );
  329.     },
  330.  
  331.     refresh: function() {
  332.         for each (cal in this.mCalendars) {
  333.             try { cal.refresh(); } catch (e) { }
  334.         }
  335.     },
  336.  
  337.     // void modifyItem( in calIItemBase aNewItem, in calIItemBase aOldItem, in calIOperationListener aListener );
  338.     modifyItem: function (aNewItem, aOldItem, aListener) {
  339.         if (aNewItem.calendar == null) {
  340.             // XXX Can't modify item with NULL parent
  341.             throw Components.results.NS_ERROR_FAILURE;
  342.         }
  343.  
  344.         aNewItem.calendar.modifyItem (aNewItem, aOldItem, aListener);
  345.     },
  346.  
  347.     // void deleteItem( in string id, in calIOperationListener aListener );
  348.     deleteItem: function (aItem, aListener) {
  349.         if (aItem.calendar == null) {
  350.             // XXX Can't delete item with NULL parent
  351.             throw Components.results.NS_ERROR_FAILURE;
  352.         }
  353.  
  354.         aItem.calendar.deleteItem (aItem, aListener);
  355.     },
  356.  
  357.     // void addItem( in calIItemBase aItem, in calIOperationListener aListener );
  358.     addItem: function (aItem, aListener) {
  359.         this.mDefaultCalendar.addItem (aItem, aListener);
  360.     },
  361.  
  362.     // void getItem( in string aId, in calIOperationListener aListener );
  363.     getItem: function (aId, aListener) {
  364.         var cmpListener = new calCompositeGetListenerHelper(this.mCalendars.length, aListener);
  365.         for each (cal in this.mCalendars) {
  366.             cal.getItem (aId, cmpListener);
  367.         }
  368.     },
  369.  
  370.     // void getItems( in unsigned long aItemFilter, in unsigned long aCount, 
  371.     //                in calIDateTime aRangeStart, in calIDateTime aRangeEnd,
  372.     //                in calIOperationListener aListener );
  373.     getItems: function (aItemFilter, aCount, aRangeStart, aRangeEnd, aListener) {
  374.         // If there are no calendars, then we just call onOperationComplete
  375.         if (this.mCalendars.length == 0) {
  376.             aListener.onOperationComplete (this,
  377.                                            Components.results.NS_OK,
  378.                                            calIOperationListener.GET,
  379.                                            null,
  380.                                            null);
  381.             return;
  382.         }
  383.  
  384.         var cmpListener = new calCompositeGetListenerHelper(this.mCalendars.length, aListener, aCount);
  385.         for (cal in this.mCalendars) {
  386.             this.mCalendars[cal].getItems (aItemFilter, aCount, aRangeStart, aRangeEnd, cmpListener);
  387.         }
  388.     },
  389.  
  390.     startBatch: function ()
  391.     {
  392.         this.notifyObservers("onStartBatch");
  393.     },
  394.     endBatch: function ()
  395.     {
  396.         this.notifyObservers("onEndBatch");
  397.     },
  398.  
  399.     //
  400.     // observer helpers
  401.     //
  402.     notifyObservers: function(method, args) {
  403.         this.mCompositeObservers.forEach(function (o) {
  404.             try { o[method].apply(o, args); }
  405.             catch (e) { }
  406.         });
  407.     }
  408. };
  409.  
  410. // composite listener helper
  411. function calCompositeGetListenerHelper(aNumQueries, aRealListener, aMaxItems) {
  412.     this.wrappedJSObject = this;
  413.     this.mNumQueries = aNumQueries;
  414.     this.mRealListener = aRealListener;
  415.     this.mMaxItems = aMaxItems;
  416. }
  417.  
  418. calCompositeGetListenerHelper.prototype = {
  419.     mNumQueries: 0,
  420.     mRealListener: null,
  421.     mReceivedCompletes: 0,
  422.     mFinished: false,
  423.     mMaxItems: 0,
  424.     mItemsReceived: 0,
  425.  
  426.     QueryInterface: function (aIID) {
  427.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  428.             !aIID.equals(Components.interfaces.calIOperationListener))
  429.         {
  430.             throw Components.results.NS_ERROR_NO_INTERFACE;
  431.         }
  432.  
  433.         return this;
  434.     },
  435.  
  436.     onOperationComplete: function (aCalendar, aStatus, aOperationType, aId, aDetail) {
  437.         if (this.mFinished) {
  438.             dump ("+++ calCompositeGetListenerHelper.onOperationComplete: called with mFinished == true!");
  439.             return;
  440.         }
  441.  
  442.         if (!Components.isSuccessCode(aStatus)) {
  443.             // proxy this to a onGetResult
  444.             // XXX - do we want to give the real calendar? or this?
  445.             // XXX - get rid of iid param
  446.             this.mRealListener.onGetResult (aCalendar, aStatus, 
  447.                                             Components.interfaces.nsISupports,
  448.                                             aDetail, 0, []);
  449.         }
  450.  
  451.         this.mReceivedCompletes++;
  452.  
  453.         if (this.mReceivedCompletes == this.mNumQueries) {
  454.             // we're done here.
  455.             this.mRealListener.onOperationComplete (this,
  456.                                                     aOperationType,
  457.                                                     calIOperationListener.GET,
  458.                                                     null,
  459.                                                     null);
  460.             this.mFinished = true;
  461.         }
  462.     },
  463.  
  464.     onGetResult: function (aCalendar, aStatus, aItemType, aDetail, aCount, aItems) {
  465.         if (this.mFinished) {
  466.             dump ("+++ calCompositeGetListenerHelper.onGetResult: called with mFinished == true!");
  467.             return;
  468.         }
  469.  
  470.         // ignore if we have a max and we're past it
  471.         if (this.mMaxItems && this.mItemsReceived >= this.mMaxItems)
  472.             return;
  473.  
  474.         if (Components.isSuccessCode(aStatus) &&
  475.             this.mMaxItems &&
  476.             ((this.mItemsReceived + aCount) > this.mMaxItems))
  477.         {
  478.             // this will blow past the limit
  479.             aCount = this.mMaxItems - this.mItemsReceived;
  480.             aItems = aItems.slice(0, numToSend);
  481.         }
  482.  
  483.         // send GetResults to the real listener
  484.         this.mRealListener.onGetResult (aCalendar, aStatus, aItemType, aDetail, aCount, aItems);
  485.         this.mItemsReceived += aCount;
  486.     }
  487.  
  488. };
  489.  
  490.  
  491.  
  492. /****
  493.  **** module registration
  494.  ****/
  495.  
  496. var calCompositeCalendarModule = {
  497.     mCID: Components.ID("{aeff788d-63b0-4996-91fb-40a7654c6224}"),
  498.     mContractID: "@mozilla.org/calendar/calendar;1?type=composite",
  499.  
  500.     mUtilsLoaded: false,
  501.     loadUtils: function compositeLoadUtils() {
  502.         if (this.mUtilsLoaded)
  503.             return;
  504.  
  505.         const jssslContractID = "@mozilla.org/moz/jssubscript-loader;1";
  506.         const jssslIID = Components.interfaces.mozIJSSubScriptLoader;
  507.  
  508.         const iosvcContractID = "@mozilla.org/network/io-service;1";
  509.         const iosvcIID = Components.interfaces.nsIIOService;
  510.  
  511.         var loader = Components.classes[jssslContractID].getService(jssslIID);
  512.         var iosvc = Components.classes[iosvcContractID].getService(iosvcIID);
  513.  
  514.         // Note that unintuitively, __LOCATION__.parent == .
  515.         // We expect to find utils in ./../js
  516.         var appdir = __LOCATION__.parent.parent;
  517.         appdir.append("js");
  518.         var scriptName = "calUtils.js";
  519.  
  520.         var f = appdir.clone();
  521.         f.append(scriptName);
  522.  
  523.         try {
  524.             var fileurl = iosvc.newFileURI(f);
  525.             loader.loadSubScript(fileurl.spec, this.__parent__.__parent__);
  526.         } catch (e) {
  527.             dump("Error while loading " + fileurl.spec + "\n");
  528.             throw e;
  529.         }
  530.  
  531.         this.mUtilsLoaded = true;
  532.     },
  533.     
  534.     registerSelf: function (compMgr, fileSpec, location, type) {
  535.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  536.         compMgr.registerFactoryLocation(this.mCID,
  537.                                         "Calendar composite provider",
  538.                                         this.mContractID,
  539.                                         fileSpec,
  540.                                         location,
  541.                                         type);
  542.     },
  543.  
  544.     getClassObject: function (compMgr, cid, iid) {
  545.         if (!cid.equals(this.mCID))
  546.             throw Components.results.NS_ERROR_NO_INTERFACE;
  547.  
  548.         if (!iid.equals(Components.interfaces.nsIFactory))
  549.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  550.  
  551.         this.loadUtils();
  552.  
  553.         return this.mFactory;
  554.     },
  555.  
  556.     mFactory: {
  557.         createInstance: function (outer, iid) {
  558.             if (outer != null)
  559.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  560.             return (new calCompositeCalendar()).QueryInterface(iid);
  561.         }
  562.     },
  563.  
  564.     canUnload: function(compMgr) {
  565.         return true;
  566.     }
  567. };
  568.  
  569. function NSGetModule(compMgr, fileSpec) {
  570.     return calCompositeCalendarModule;
  571. }
  572.